package circularity;

/**
 * This class contains tests for self- and mutually- referential 
 * data definitions to ensure that circularity in data structures is 
 * handled appropriately when checking for equality
 * 
 * @author Sachin Venugopalan
 * @since 27 February, 2012
 */
import tester.*;

import java.util.*;

@Example
public class CircularExamples implements IExamples  {

	private String desc = "";

	private AuthorCirc a1;
	private AuthorCirc a2;
	private BookCirc b1;
	private BookCirc b2;

	private List alist1;
	private List alist2;

	private AuthorCircMany aa1;
	private AuthorCircMany aa2;
	private AuthorCircMany aa3;
	private AuthorCircMany aa4;
	private BookCircMany bb1;
	private BookCircMany bb2;
	private BookCircMany bb3;
	private BookCircMany bb4;

	private AuthorCirc getNewAuthorFromTemplate() {
		return new AuthorCirc("Mohandas Karamchand Gandhi");
	}

	private BookCirc getNewBookFromTemplate() {
		return new BookCirc("The Story of My Experiments with Truth", 1925);
	}

	private AuthorCircMany getNewAuthorListFromTemplate() {
		return new AuthorCircMany("Mohandas Karamchand Gandhi");
	}

	private BookCircMany getNewBookListFromTemplate() {
		return new BookCircMany("The Story of My Experiments with Truth", 1925);
	}


	@TestMethod
	/*
	 * Scenario #1 (mutually-referential objects, normal case): 
	 * a1 <--> b1, a2 <--> b2
	 * 
	 * Note: self-referential object tests a special case of 
	 * mutually-referential objects (disregarding intermediate linking object)
	 */
	private void testScenario1(Tester t) {
		desc = "Scenario #1 (mutually-referential objects, normal case):\n"
				+ "a1 <--> b1, a2 <--> b2\n";

		a1.setBook(b1);
		a2.setBook(b2);

		b1.setAuthor(a1);
		b2.setAuthor(a2);
		
		t.checkExpect(a1, a2, "t.checkExpect(a1, a2)::\n" + desc);
	}

	@TestMethod
	/*
	 * Scenario #2 (mutually-referential objects, cross-referenced): 
	 * a1 --> b1, a2 --> b2; 
	 * b1 --> a2, b2 --> a1
	 */
	private void testScenario2(Tester t) {
		desc = "Scenario #2 (mutually-referential objects, cross-referenced):\n"
				+ "a1 --> b1, a2 --> b2;\n" + "b1 --> a2; b2 --> a1\n";

		a1.setBook(b1);
		a2.setBook(b2);

		b1.setAuthor(a2);
		b2.setAuthor(a1);
		
		t.checkExpect(a1, a2, "t.checkExpect(a1, a2)::\n" + desc);
	}

	@TestMethod
	/*
	 * Scenario #3 (self-referential list of objects): 
	 * a1 <--> b1, a2 <--> b2
	 * alist1: a1, alist1;
	 * alist2: a2, alist2
	 */
	private void testScenario3(Tester t) {
		desc = "Scenario #3 (self-referential list of objects):\n"
				+ "a1 <--> b1, a2 <--> b2;\n"
				+ "alist1: a1, alist1;\n"
				+ "alist2: a2, alist2\n";
		
		a1.setBook(b1);
		a2.setBook(b2);

		b1.setAuthor(a1);
		b2.setAuthor(a2);

		alist1.clear();
		alist1.add(a1);
		alist1.add(alist1);

		alist2.clear();
		alist2.add(a2);
		alist2.add(alist2);

		t.checkExpect(alist1, alist2, "t.checkExpect(alist1, alist1)::\n" + desc);
	}

	@TestMethod
	/*
	 * Scenario #4 (mutually-referential lists of objects): 
	 * a1 <--> b1, a2 <--> b2;
	 * alist1: a1, a2, alist2;
	 * alist2: a1, a2, alist1
	 */
	private void testScenario4(Tester t) {
		desc = "Scenario #4 (mutually-referential lists of objects):\n"
				+ "a1 <--> b1, a2 <--> b2;\n"
				+ "alist1: a1, a2, alist2;\n"
				+ "alist2: a1, a2, alist1\n";

		a1.setBook(b1);
		a2.setBook(b2);

		b1.setAuthor(a1);
		b2.setAuthor(a2);
		
		alist1.clear();
		alist1.add(alist2); 
		alist1.add(a1);

		alist2.clear();
		alist2.add(alist1);
		alist2.add(a2);
		
		t.checkExpect(alist1, alist2, "t.checkExpect(alist1, alist2)::\n" + desc);		
		/*
		 * <<NOTE>>
		 * [Author: Sachin Venugopalan; Date: 03/03/2012]
		 * The equality check 
		 *   t.checkExpect(alist1, alist2
		 * will be "stemmed"/"culled" at index 0, when the pair of objects
		 * 	 alist1[0] (i.e. alist2) 
		 * and 
		 *   alist2[0] (i.e. alist1) 
		 * are encountered; but note that their "sameness" (equality) has not yet
		 * been evaluated! This pair has already been seen before; hence the
		 * assessment of their equality is aborted though on 2 premises of reason:
		 * 
		 * 1. If a sameness assessment has already been made for them, then it must
		 * have passed as "true" since we have continued on with further assessment
		 * of the container data structure (a sameness assessment of "false" would
		 * cause a pre-empted return of "false" for the container structure). 
		 * 
		 * 2. If, on the other hand, a sameness evaluation was not completed, this is because
		 * the current evaluation is included in that assessment (circularity). This
		 * indicates the presence of cycles in the structure that must not be
		 * navigated, since termination of the original equality assessment is
		 * desirable. [Moreover, a single-threaded execution is assumed thus ruling
		 * out any "behind-your-back" mutations in values (perhaps triggered by
		 * events or time) while the original assessment is in progress. Even so,
		 * accounting for such eventualities is usually impractical and we'd be
		 * reduced to checking for equality of "precisely-timed" or
		 * "event-suspended/locked" "snapshots" of the data structures in question.]
		 * The result of such aborted evaluations will eventually be determined when 
		 * the result of the original evaluation is found (termination necessary); 
		 * moreover, those results will all match the result of the original evaluation.
		 */
	}

	@TestMethod
	/*
	 * Scenario #5 (mutually referential lists of objects, cross-references): 
	 * a1 --> b1, a2 --> b2; 
	 * b1 --> a2, b2 --> a1; 
	 * alist1: a1, a2, alist2; 
	 * alist2: a1, a2, alist1
	 */
	private void testScenario5(Tester t) {
		desc = "Scenario #5 (mutually referential lists of objects, cross-references):\n"
				+ "a1 <--> b1, a2 <--> b2;\n"
				+ "b1 --> a2, b2 --> a1;\n"
				+ "alist1: a1, a2, alist2;\n"
				+ "alist2: a1, a2, alist1\n";

		a1.setBook(b1);
		a2.setBook(b2);

		b1.setAuthor(a2);
		b2.setAuthor(a1);
		
		alist1.clear();
		alist1.add(a1);
		alist1.add(a2);
		alist1.add(alist2);

		alist2.clear();
		alist2.add(a2);
		alist2.add(a1);
		alist2.add(alist1);

		t.checkExpect(alist1, alist2, "t.checkExpect(alist1, alist2)::\n" + desc);
	}

	@TestMethod
	/*
	 * Scenario #6 (mutually-referential objects-with-lists, normal case, distinct lists): 
	 * aa1, aa2 <--> bb1, bb2; 
	 * aa3, aa4 <--> bb3, bb4
	 */
	private void testScenario6(Tester t) {
		desc = "Scenario #6 (mutually-referential objects-with-lists, normal case):\n"
				+ "aa1, aa2 <--> bb1, bb2;\n"
				+ "aa3, aa4 <--> bb3, bb4\n";

		aa1.books = new ArrayList<BookCircMany>();
		aa1.books.add(bb1);
		aa1.books.add(bb2);
		aa2.books = new ArrayList<BookCircMany>();
		aa2.books.add(bb1);
		aa2.books.add(bb2);
		
		bb1.authors = new ArrayList<AuthorCircMany>();
		bb1.authors.add(aa1);
		bb1.authors.add(aa2);
		bb2.authors = new ArrayList<AuthorCircMany>();
		bb2.authors.add(aa1);
		bb2.authors.add(aa2);

		aa3.books = new ArrayList<BookCircMany>();
		aa3.books.add(bb3);
		aa3.books.add(bb4);
		aa4.books = new ArrayList<BookCircMany>();
		aa4.books.add(bb3);
		aa4.books.add(bb4);
		
		bb3.authors = new ArrayList<AuthorCircMany>();
		bb3.authors.add(aa3);
		bb3.authors.add(aa4);
		bb4.authors = new ArrayList<AuthorCircMany>();
		bb4.authors.add(aa3);
		bb4.authors.add(aa4);
		
		t.checkExpect(aa1, aa3, "t.checkExpect(aa1, aa3)::\n" + desc);
	}

	@TestMethod
	/*
	 * Scenario #7 (mutually-referential objects-with-lists, normal case, same lists): 
	 * (aa1, aa2) <--> (bb1, bb2); 
	 * (aa3, aa4) <--> (bb3, bb4)
	 */
	private void testScenario7(Tester t) {
		desc = "Scenario #7 (mutually-referential objects-with-lists, normal case, same lists):\n"
				+ "(aa1, aa2) <--> (bb1, bb2);\n"
				+ "(aa3, aa4) <--> (bb3, bb4)\n";

		aa1.books = new ArrayList<BookCircMany>();
		aa1.books.add(bb1);
		aa1.books.add(bb2);
		aa2.books = aa1.books;
		
		bb1.authors = new ArrayList<AuthorCircMany>();
		bb1.authors.add(aa1);
		bb1.authors.add(aa2);
		bb2.authors = bb1.authors;

		aa3.books = new ArrayList<BookCircMany>();
		aa3.books.add(bb3);
		aa3.books.add(bb4);
		aa4.books = aa3.books;
		
		bb3.authors = new ArrayList<AuthorCircMany>();
		bb3.authors.add(aa3);
		bb3.authors.add(aa4);
		bb4.authors = bb3.authors;
		
		t.checkExpect(aa1, aa3, "t.checkExpect(aa1, aa3)::\n" + desc);
	}

	@TestMethod
	/*
	 * Scenario #8 (mutually-referential objects-with-lists, cross-references, same lists): 
	 * (aa1, aa2) --> (bb1, bb2) --> (aa3, aa4);
	 * (aa3, aa4) --> (bb3, bb4) --> (aa1, aa2)
	 */
	private void testScenario8(Tester t) {
		desc = "Scenario #8 (mutually-referential objects-with-lists, cross-references, same lists):\n"
				+ "(aa1, aa2) --> (bb1, bb2) --> (aa3, aa4);\n"
				+ "(aa3, aa4) --> (bb3, bb4) --> (aa1, aa2)\n";

		aa1.books = new ArrayList<BookCircMany>();
		aa1.books.add(bb1);
		aa1.books.add(bb2);
		aa2.books = aa1.books;

		aa3.books = new ArrayList<BookCircMany>();
		aa3.books.add(bb3);
		aa3.books.add(bb4);
		aa4.books = aa3.books;
		
		bb1.authors = new ArrayList<AuthorCircMany>();
		bb1.authors.add(aa3);
		bb1.authors.add(aa4);
		bb2.authors = bb1.authors;

		bb3.authors = new ArrayList<AuthorCircMany>();
		bb3.authors.add(aa1);
		bb3.authors.add(aa2);
		bb4.authors = bb3.authors;
		
		t.checkExpect(aa1, aa3, "t.checkExpect(aa1, aa3)::\n" + desc);
	}


	@TestMethod
	/*
	 * Scenario #9 (mutually-referential objects-with-lists, cross-references + complications): 
	 * (aa1, *) --> (bb1, bb2) 
	 * (*, aa2) --> (bb3, bb4)
	 * (aa3, *) --> (bb3, bb4)
	 * (*, aa4) --> (bb1, bb2)
	 * 
	 * (bb1, *) --> (aa3, aa4)
	 * (*, bb2) --> (aa1, aa2)
	 * (bb3, *) --> (aa1, aa2)
	 * (*, bb4) --> (aa3, aa4)
	 */
	private void testScenario9(Tester t) {
		desc = "Scenario #9 (mutually-referential objects-with-lists, cross-references + complications):\n"
				+ "(aa1, *) --> (bb1, bb2)\n"
				+ "(*, aa2) --> (bb3, bb4)\n"
                + "(aa3, *) --> (bb3, bb4)\n"
		        + "(*, aa4) --> (bb1, bb2)\n"
		        + "\n"
		        + "(bb1, *) --> (aa3, aa4)\n"
		        + "(*, bb2) --> (aa1, aa2)\n"
		        + "(bb3, *) --> (aa1, aa2)\n"
		        + "(*, bb4) --> (aa3, aa4)\n";	
		
		aa1.books = new ArrayList<BookCircMany>();
		aa1.books.add(bb1);
		aa1.books.add(bb2);

		aa3.books = new ArrayList<BookCircMany>();
		aa3.books.add(bb3);
		aa3.books.add(bb4);
		
		aa2.books = aa3.books;
		aa4.books = aa1.books;
		
		bb1.authors = new ArrayList<AuthorCircMany>();
		bb1.authors.add(aa3);
		bb1.authors.add(aa4);

		bb3.authors = new ArrayList<AuthorCircMany>();
		bb3.authors.add(aa1);
		bb3.authors.add(aa2);
		
		bb2.authors = bb3.authors;
		bb4.authors = bb1.authors;
		
		t.checkExpect(aa1, aa3, "t.checkExpect(aa1, aa3)::\n" + desc);
	}

	public CircularExamples() {

		a1 = getNewAuthorFromTemplate();
		a2 = getNewAuthorFromTemplate();
		
		b1 = getNewBookFromTemplate();
		b2 = getNewBookFromTemplate();

		alist1 = new ArrayList();
		alist2 = new ArrayList();

		aa1 = getNewAuthorListFromTemplate();
		aa2 = getNewAuthorListFromTemplate();
		aa3 = getNewAuthorListFromTemplate();
		aa4 = getNewAuthorListFromTemplate();

		bb1 = getNewBookListFromTemplate();
		bb2 = getNewBookListFromTemplate();
		bb3 = getNewBookListFromTemplate();
		bb4 = getNewBookListFromTemplate();
		
	}

	@Override
	public void tests(Tester t) {

		testScenario1(t);
		testScenario2(t);
		testScenario3(t);
		testScenario4(t);
		testScenario5(t);
		testScenario6(t);
		testScenario7(t);
		testScenario8(t);
		testScenario9(t);
		
	}

	public static void main(String[] args) {
		Tester.runFullReport(new CircularExamples());
	}
}
